﻿using System;
using System.Linq;
using System.Text;
using Ewk.Configuration;
using Ewk.Math.Algebra.Algorithms.Matrices;
using Ewk.Math.ComputationTypes;

namespace Ewk.Math.Algebra
{
    /// <summary>
    /// Represents a matrix with elements of <see cref="Type"/> <see cref="Computable{T}"/>.
    /// </summary>
    /// <typeparam name="T">The <see cref="Type"/> of the internal value of the elements.</typeparam>
    public class Matrix<T> : IEquatable<Matrix<T>>
        where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
    {
        private Computable<T>[,] _elements;
        private Computable<T> _determinant;
        private Computable<T> _trace;
        private int? _rank;
        private bool _transposed;

        #region Constructors

        /// <summary>
        /// Initializes a new instance with m by m elements.
        /// </summary>
        /// <param name="m">The number of rows and colums.</param>
        public Matrix(int m)
            : this(m, m)
        { }

        /// <summary>
        /// <summary>
        /// Initializes a new instance with rowCount by columnCount elements.
        /// </summary>
        /// </summary>
        /// <param name="rowCount">The number of rows.</param>
        /// <param name="columnCount">The number of columns.</param>
        public Matrix(int rowCount, int columnCount)
            : this(new Computable<T>[rowCount, columnCount])
        {
        }

        /// <summary>
        /// Initializes a new instance with the specified elements.
        /// </summary>
        /// <param name="elements">The elements of the <see cref="Matrix{T}"/>.</param>
        public Matrix(Computable<T>[,] elements)
        {
            _elements = elements;
        }

        /// <summary>
        /// Initializes a new instance with the specified internal values.
        /// </summary>
        /// <param name="values">The internal values of the elements of the <see cref="Matrix{T}"/>.</param>
        public Matrix(T[,] values)
        {
            var rowCount = values.GetLength(0);
            var columnCount = values.GetLength(1);

            _elements = new Computable<T>[rowCount,columnCount];
            Iterate(rowCount, columnCount,
                    (i, j) =>
                    _elements[i, j] = Computable<T>.Factory.Create(values[i, j]));
        }

        #endregion

        /// <summary>
        /// Gets the rows of the matrix.
        /// </summary>
        private Computable<T>[,] Elements
        {
            get { return _elements; }
            set
            {
                _determinant = null;
                _trace = null;
                _rank = null;
                _transposed = false;

                _elements = value;
            } 
        }

        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        public Computable<T> this[int rowIndex, int columnIndex]
        {
            get
            {
                return _transposed
                           ? Elements[columnIndex, rowIndex]
                           : Elements[rowIndex, columnIndex];
            }
            set
            {
                _determinant = null;
                _rank = null;

                if (rowIndex == columnIndex)
                {
                    _trace = null;
                }

                if (_transposed)
                {
                    Elements[columnIndex, rowIndex] = value;
                }
                else
                {
                    Elements[rowIndex, columnIndex] = value;
                }
            }
        }

        /// <summary>
        /// Gets a row of the matrix.
        /// </summary>
        public Vector<T> GetRow(int index)
        {
            var vector = new Vector<T>(ColumnCount);

            Vector<T>.Iterate(vector.Length, i => vector[i] = this[index, i]);

            return vector;
        }

        /// <summary>
        /// Gets a column of the matrix.
        /// </summary>
        public Vector<T> GetColumn(int index)
        {
            var vector = new Vector<T>(RowCount);

            Vector<T>.Iterate(vector.Length, i => vector[i] = this[i, index]);

            return vector;
        }

        /// <summary>
        /// Gets the row dimension of the matrix.
        /// </summary>
        public int RowCount
        {
            get
            {
                return _transposed
                           ? Elements.GetLength(1)
                           : Elements.GetLength(0);
            }
        }

        /// <summary>
        /// Gets the column dimension of the matrix.
        /// </summary>
        public int ColumnCount
        {
            get
            {
                return _transposed
                           ? Elements.GetLength(0)
                           : Elements.GetLength(1);
            }
        }

        /// <summary>
        /// Gets a value that indicates if this matrix is a squared matrix.
        /// </summary>
        public bool IsSquared
        {
            get { return RowCount == ColumnCount; }
        }

        /// <summary>
        /// Swaps the rows specified by the provided indexes.
        /// </summary>
        /// <param name="index1">The index of the first row.</param>
        /// <param name="index2">The index of the second row.</param>
        public Matrix<T> SwapRows(int index1, int index2)
        {
            Vector<T>.Iterate(ColumnCount,
                              i =>
                                  {
                                      var tempValue = this[index1, i];
                                      this[index1, i] = this[index2, i];
                                      this[index2, i] = tempValue;
                                  });

            var determinantManipulator = GetDeterminantManipulator(this);
            _determinant = determinantManipulator.SwapRows(index1, index2);

            return this;
        }

        /// <summary>
        /// Swaps the columns specified by the provided indexes.
        /// </summary>
        /// <param name="index1">The index of the first column.</param>
        /// <param name="index2">The index of the second column.</param>
        public Matrix<T> SwapColumns(int index1, int index2)
        {
            Vector<T>.Iterate(RowCount,
                              i =>
                                  {
                                      var tempValue = this[i, index1];
                                      this[i, index1] = this[i, index2];
                                      this[i, index2] = tempValue;
                                  });

            var determinantManipulator = GetDeterminantManipulator(this);
            _determinant = determinantManipulator.SwapColumns(index1, index2);

            return this;
        }

        #region Computing
        /// <summary>
        /// Adds the provided <paramref name="value"/> to each element of this matrix
        /// </summary>
        /// <param name="value">The value to add.</param>
        public Matrix<T> Add(Computable<T> value)
        {
            var result = Add(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Adds the provided <paramref name="matrix"/> to this matrix
        /// </summary>
        /// <param name="matrix">The matrix to add.</param>
        public Matrix<T> Add(Matrix<T> matrix)
        {
            var result = Add(this, matrix);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Subtracts the provided <paramref name="value"/> from each element of this matrix
        /// </summary>
        /// <param name="value">The value to subtract.</param>
        public Matrix<T> Subtract(Computable<T> value)
        {
            var result = Subtract(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Subtracts the provided <paramref name="matrix"/> from this matrix
        /// </summary>
        /// <param name="matrix">The matrix to subtract.</param>
        public Matrix<T> Subtract(Matrix<T> matrix)
        {
            var result = Subtract(this, matrix);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Multiplies the provided <paramref name="value"/> with each element of this matrix
        /// </summary>
        /// <param name="value">The value to multiply with.</param>
        public Matrix<T> Multiply(Computable<T> value)
        {
            var result = Multiply(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Multiplies the provided <paramref name="matrix"/> with this matrix
        /// </summary>
        /// <param name="matrix">The matrix to multiply with.</param>
        public Matrix<T> Multiply(Matrix<T> matrix)
        {
            var result = Multiply(this, matrix);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Divides each element of this matrix by provided <paramref name="value"/>.
        /// </summary>
        /// <param name="value">The value to divide with.</param>
        public Matrix<T> Divide(Computable<T> value)
        {
            var result = Divide(this, value);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Transposes this matrix.
        /// </summary>
        public Matrix<T> Transpose()
        {
            _transposed = !_transposed;

            return this;
        }

        /// <summary>
        /// Gets the trace of this matrix.
        /// </summary>
        public Computable<T> Trace
        {
            get { return _trace ?? (_trace = ComputeTrace(this)); }
        }

        /// <summary>
        /// Gets the inverse of this matrix.
        /// </summary>
        public Matrix<T> Invert()
        {
            var result = ComputeInverse(this);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Gets the adjugate of this matrix.
        /// </summary>
        public Matrix<T> Adjugate()
        {
            var result = ComputeAdjugate(this);
            CopyState(result);

            return this;
        }

        /// <summary>
        /// Gets the determinant of this matrix.
        /// </summary>
        public Computable<T> Determinant
        {
            get { return _determinant ?? (_determinant = ComputeDeterminant(this)); }
        }

        /// <summary>
        /// Gets the rank of this matrix.
        /// </summary>
        public int Rank
        {
            get
            {
                if (!_rank.HasValue)
                {
                    _rank = ComputeRank(this);
                }

                return _rank.Value;
            }
        }
        #endregion

        #region Equality
        /// <summary>
        /// Returns a value indicating whether this instance and the specified <see cref="Matrix{T}"/> represent the same value.
        /// </summary>
        /// <param name="other">The <see cref="Matrix{T}"/> to compare with.</param>
        /// <returns>A value indicating whether this instance and the specified <see cref="Matrix{T}"/> represent the same value.</returns>
        public bool Equals(Matrix<T> other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;

            for (var i = 0; i < RowCount; i++)
            {
                for (var j = 0; j < ColumnCount; j++)
                {
                    if (!this[i, j].Equals(other[i, j]))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// Returns a value indicating whether this instance and the specified <see cref="object"/> represent the same value.
        /// </summary>
        /// <param name="obj">The <see cref="object"/> to compare with.</param>
        /// <returns>A value indicating whether this instance and the specified <see cref="object"/> represent the same value.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Matrix<T>);
        }

        /// <summary>
        /// Serves as a hash function.
        /// </summary>
        /// <returns>An <see cref="int"/> that represents the hash of this instance.</returns>
        public override int GetHashCode()
        {
            return (Elements != null ? Elements.GetHashCode() : 0);
        }
        #endregion

        /// <summary>
        /// Creates a <see cref="string"/> representation  of the <see cref="Matrix{T}"/> instance.
        /// </summary>
        /// <returns>A <see cref="string"/> representation  of the <see cref="Matrix{T}"/> instance.</returns>
        public override string ToString()
        {
            var sb = new StringBuilder();

            Vector<T>.Iterate(RowCount, i =>
                                            {
                                                sb.Append(GetRow(i).ToString());
                                                sb.Append("\n");
                                            });

            return sb.ToString();
        }

        private void CopyState(Matrix<T> other)
        {
            Elements = other.Elements;
            _determinant = other._determinant;
            _rank = other._rank;
            _trace = other._trace;
            _transposed = other._transposed;
        }

        #region Static Members

        #region Computing
        /// <summary>
        /// Adds a <see cref="Computable{T}"/> instance to every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> Add(Matrix<T> matrix, Computable<T> value)
        {
            if (matrix == null) return null;
            if (value == null)
            {
                value = Computable<T>.Factory.Zero;
            }

            var elements = new Computable<T>[matrix.RowCount, matrix.ColumnCount];
            Iterate(matrix.RowCount, matrix.ColumnCount, (i, j) => elements[i, j] = matrix[i, j] + value);

            var determinantManipulator = GetDeterminantManipulator(matrix);
            return new Matrix<T>(elements)
            {
                _determinant = determinantManipulator.Add(value)
            };
        }

        /// <summary>
        /// Adds two <see cref="Matrix{T}"/> instances.
        /// </summary>
        public static Matrix<T> Add(Matrix<T> a, Matrix<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                if (a == null)
                {
                    a = Zero(b.RowCount, b.ColumnCount);
                }
                else
                {
                    b = Zero(a.RowCount, a.ColumnCount);
                }
            }

            var elements = new Computable<T>[a.RowCount, a.ColumnCount];
            Iterate(a, b, (i, j) => elements[i, j] = a[i, j] + b[i, j]);

            var determinantManipulator = GetDeterminantManipulator(a);
            return new Matrix<T>(elements)
            {
                _determinant = determinantManipulator.Add(b)
            };
        }

        /// <summary>
        /// Subtracts a <see cref="Computable{T}"/> instance from every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> Subtract(Matrix<T> matrix, Computable<T> value)
        {
            if (matrix == null) return null;
            if (value == null)
            {
                value = Computable<T>.Factory.Zero;
            }

            var elements = new Computable<T>[matrix.RowCount, matrix.ColumnCount];
            Iterate(matrix.RowCount, matrix.ColumnCount, (i, j) => elements[i, j] = matrix[i, j] - value);

            var determinantManipulator = GetDeterminantManipulator(matrix);
            return new Matrix<T>(elements)
            {
                _determinant = determinantManipulator.Subtract(value)
            };
        }

        /// <summary>
        /// Subtracts <see cref="Matrix{T}">b</see> from <see cref="Matrix{T}">a</see>.
        /// </summary>
        public static Matrix<T> Subtract(Matrix<T> a, Matrix<T> b)
        {
            if (a == null || b == null)
            {
                if (a == null && b == null)
                {
                    return null;
                }

                if (a == null)
                {
                    a = Zero(b.RowCount, b.ColumnCount);
                }
                else
                {
                    b = Zero(a.RowCount, a.ColumnCount);
                }
            }

            var elements = new Computable<T>[a.RowCount, a.ColumnCount];
            Iterate(a, b, (i, j) => elements[i, j] = a[i, j] - b[i, j]);

            var determinantManipulator = GetDeterminantManipulator(a);
            return new Matrix<T>(elements)
                {
                    _determinant = determinantManipulator.Subtract(b)
                };
        }

        /// <summary>
        /// Multiplies a <see cref="Computable{T}"/> instance with every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> Multiply(Matrix<T> matrix, Computable<T> value)
        {
            if (matrix == null) return null;
            if (value == null)
            {
                return Zero(matrix.RowCount, matrix.ColumnCount);
            }

            var elements = new Computable<T>[matrix.RowCount, matrix.ColumnCount];
            Iterate(matrix.RowCount, matrix.ColumnCount, (i, j) => elements[i, j] = matrix[i, j] * value);

            var determinantManipulator = GetDeterminantManipulator(matrix);
            return new Matrix<T>(elements)
                             {
                                 _trace = matrix._trace*value,
                                 _determinant = determinantManipulator.Multiply(value)
                             };
        }

        /// <summary>
        /// Transforms <see cref="Matrix{T}">b</see> by multiplying it 
        /// with <see cref="Matrix{T}">a</see>.
        /// </summary>
        public static Matrix<T> Multiply(Matrix<T> a, Matrix<T> b)
        {
            if (a == null || b == null)
            {
                return null;
            }

            var elements = new Computable<T>[a.RowCount, b.ColumnCount];
            Iterate(a.RowCount, b.ColumnCount, (i, j) => elements[i, j] = a.GetRow(i) * b.GetColumn(j));

            var determinantManipulator = GetDeterminantManipulator(a);
            return new Matrix<T>(elements)
                             {
                                 _determinant = determinantManipulator.Multiply(b)
                             };
        }

        /// <summary>
        /// Divides every element of the provided <see cref="Matrix{T}"/> instance
        /// by the provided <see cref="Computable{T}"/> instance.
        /// </summary>
        public static Matrix<T> Divide(Matrix<T> matrix, Computable<T> value)
        {
            if (matrix == null) return null;
            if (value == null)
            {
                throw new InvalidOperationException("Cannot divide by nothing.");
            }

            var elements = new Computable<T>[matrix.RowCount, matrix.ColumnCount];
            Iterate(matrix.RowCount, matrix.ColumnCount, (i, j) => elements[i, j] = matrix[i, j] / value);

            var determinantManipulator = GetDeterminantManipulator(matrix);
            return new Matrix<T>(elements)
                {
                    _trace = matrix._trace/value,
                    _determinant = determinantManipulator.Divide(value)
                };
        }

        /// <summary>
        /// Calculates the transposed representation of the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to calculate the transposed <see cref="Matrix{T}"/> for.</param>
        /// <returns>The transposed representation of the specified <see cref="Matrix{T}"/>.</returns>
        public static Matrix<T> Transpose(Matrix<T> matrix)
        {
            if (matrix == null) return null;

            var elements = new Computable<T>[matrix.ColumnCount, matrix.RowCount];
            Iterate(matrix.RowCount, matrix.ColumnCount, (i, j) => elements[j, i] = matrix[i, j]);

            var determinantManipulator = GetDeterminantManipulator(matrix);
            return new Matrix<T>(elements)
                       {
                           _trace = matrix._trace,
                           _determinant = determinantManipulator.Transpose()
                       };
        }

        /// <summary>
        /// Computes the trace of the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to calculate the trace from.</param>
        /// <returns>A <see cref="Computable{T}"/> that represents the trace of the specified <see cref="Matrix{T}"/>.</returns>
        public static Computable<T> ComputeTrace(Matrix<T> matrix)
        {
            if (matrix == null) return null;

            var result = Computable<T>.Factory.Zero;

            Iterate(matrix, i => result += matrix[i, i]);

            return result;
        }

        /// <summary>
        /// Calculates the inverse representation of the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to calculate the inverse <see cref="Matrix{T}"/> for.</param>
        /// <returns>The inverse representation of the specified <see cref="Matrix{T}"/>.</returns>
        public static Matrix<T> ComputeInverse(Matrix<T> matrix)
        {
            var computer = DependencyConfiguration.DependencyResolver.Resolve<IInverseComputer<T>>();
            var inverse = computer.ComputeInverse(matrix);

            var determinantManipulator = GetDeterminantManipulator(matrix);
            inverse._determinant = determinantManipulator.Invert();

            return inverse;
        }

        /// <summary>
        /// Computes the determinant of the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to calculate the determinant from.</param>
        /// <returns>A <see cref="Computable{T}"/> that represents the determinant of the specified <see cref="Matrix{T}"/>.</returns>
        public static Computable<T> ComputeDeterminant(Matrix<T> matrix)
        {
            var computer = DependencyConfiguration.DependencyResolver.Resolve<IDeterminantComputer<T>>();
            return computer.ComputeDeterminant(matrix);
        }

        /// <summary>
        /// Computes the rank of the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to calculate the rank from.</param>
        /// <returns>A <see cref="Computable{T}"/> that represents the rank of the specified <see cref="Matrix{T}"/>.</returns>
        public static int ComputeRank(Matrix<T> matrix)
        {
            var computer = DependencyConfiguration.DependencyResolver.Resolve<IRankComputer<T>>();
            return computer.ComputeRank(matrix);
        }

        /// <summary>
        /// Computes the adjugate of the specified <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="matrix">The <see cref="Matrix{T}"/> to calculate the adjugate from.</param>
        /// <returns>A <see cref="Matrix{T}"/> that represents the adjugate of the specified <see cref="Matrix{T}"/>.</returns>
        public static Matrix<T> ComputeAdjugate(Matrix<T> matrix)
        {
            var computer = DependencyConfiguration.DependencyResolver.Resolve<IAdjugateComputer<T>>();
            return computer.ComputeAdjugate(matrix);
        }

        /// <summary>
        /// Creates a minor <see cref="Matrix{T}"/> of the specified source <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="source">The <see cref="Matrix{T}"/> to use to create the minor <see cref="Matrix{T}"/> from.</param>
        /// <param name="omittedRowNumber">The number of the row in the source <see cref="Matrix{T}"/> that will be omitted in the minor <see cref="Matrix{T}"/>.</param>
        /// <param name="omittedColumnNumber">The number of the column in the source <see cref="Matrix{T}"/> that will be omitted in the minor <see cref="Matrix{T}"/>.</param>
        /// <returns>
        /// A <see cref="Matrix{T}"/> that contains all the elements of the source <see cref="Matrix{T}"/>
        /// excluded the specified <paramref name="omittedRowNumber"/> and <paramref name="omittedColumnNumber"/>.
        /// </returns>
        public static Matrix<T> Minor(Matrix<T> source, int omittedRowNumber, int omittedColumnNumber)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            var minor = new Matrix<T>(source.RowCount - 1, source.ColumnCount - 1);

            var ii = 0;
            for (var i = 0; i < source.RowCount; i++)
            {
                if (i == omittedRowNumber) continue;

                var jj = 0;
                for (var j = 0; j < source.ColumnCount; j++)
                {
                    if (j == omittedColumnNumber) continue;

                    minor[ii, jj] = source[i, j];
                    jj++;
                }
            
                ii++;
            }
            
            return minor;
        }

        #endregion

        /// <summary>
        /// Represents the zero <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="rowCount">The number of rows.</param>
        /// <param name="columnCount">The number of columns.</param>
        /// <returns>The zero <see cref="Matrix{T}"/>.</returns>
        public static Matrix<T> Zero(int rowCount, int columnCount)
        {
            var elements = new Computable<T>[rowCount, columnCount];

            Iterate(rowCount, columnCount,
                    (i, j) => elements[i, j] = Computable<T>.Factory.Zero
                );

            return new Matrix<T>(elements);
        }

        /// <summary>
        /// Represents the identity <see cref="Matrix{T}"/>.
        /// </summary>
        /// <param name="m">The number of rows and columns.</param>
        /// <returns>The identity <see cref="Matrix{T}"/>.</returns>
        public static Matrix<T> Identity(int m)
        {
            var elements = new Computable<T>[m, m];

            Iterate(m, m,
                    (i, j) => elements[i, j] =
                              i == j
                                  ? Computable<T>.Factory.One
                                  : Computable<T>.Factory.Zero
                );

            return new Matrix<T>(elements);
        }

        #region Overloaded operators

        /// <summary>
        /// Adds two <see cref="Matrix{T}"/> instances.
        /// </summary>
        public static Matrix<T> operator +(Matrix<T> a, Matrix<T> b)
        {
            return Add(a, b);
        }

        /// <summary>
        /// Adds a <see cref="Computable{T}"/> instance to every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> operator +(Matrix<T> matrix, Computable<T> value)
        {
            return Add(matrix, value);
        }

        /// <summary>
        /// Subtracts <see cref="Matrix{T}">b</see> from <see cref="Matrix{T}">a</see>.
        /// </summary>
        public static Matrix<T> operator -(Matrix<T> a, Matrix<T> b)
        {
            return Subtract(a, b);
        }

        /// <summary>
        /// Subtracts a <see cref="Computable{T}"/> instance from every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> operator -(Matrix<T> matrix, Computable<T> value)
        {
            return Subtract(matrix, value);
        }

        /// <summary>
        /// Transforms <see cref="Matrix{T}">b</see> by multiplying it 
        /// with <see cref="Matrix{T}">a</see>.
        /// </summary>
        public static Matrix<T> operator *(Matrix<T> a, Matrix<T> b)
        {
            return Multiply(a, b);
        }

        /// <summary>
        /// Multiplies a <see cref="Computable{T}"/> instance with every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> operator *(Matrix<T> matrix, Computable<T> value)
        {
            return Multiply(matrix, value);
        }

        /// <summary>
        /// Multiplies a <see cref="Computable{T}"/> instance with every element
        /// of the specified <see cref="Matrix{T}"/> instance.
        /// </summary>
        public static Matrix<T> operator *(Computable<T> value, Matrix<T> matrix)
        {
            return Multiply(matrix, value);
        }

        /// <summary>
        /// Divides every element of the provided <see cref="Matrix{T}"/> instance
        /// by the provided <see cref="Computable{T}"/> instance.
        /// </summary>
        public static Matrix<T> operator /(Matrix<T> matrix, Computable<T> value)
        {
            return Divide(matrix, value);
        }

        /// <summary>
        /// Returns a value that indicates if <see cref="Matrix{T}">a</see> is equal to <see cref="Matrix{T}">b</see>.
        /// </summary>
        public static bool operator ==(Matrix<T> a, Matrix<T> b)
        {
            if (ReferenceEquals(null, a))
            {
                return ReferenceEquals(null, b) || b.Equals(null);
            }

            return a.Equals(b);
        }

        /// <summary>
        /// Returns a value that indicates if <see cref="Matrix{T}">a</see> is not equal to <see cref="Matrix{T}">b</see>.
        /// </summary>
        public static bool operator !=(Matrix<T> a, Matrix<T> b)
        {
            return !(a == b);
        }

        #endregion

        internal static void Iterate(Matrix<T> matrix, Action<int> action)
        {
            var minDimension = System.Math.Min(matrix.RowCount, matrix.ColumnCount);

            for (var i = 0; i < minDimension; i++)
            {
                action(i);
            }
        }

        internal static void Iterate(int rowCount, int columnCount, Action<int, int> action)
        {
            for (var i = 0; i < rowCount; i++)
            {
                for (var j = 0; j < columnCount; j++)
                {
                    action(i, j);
                }
            }
        }

        internal static void Iterate(Matrix<T> a, Matrix<T> b, Action<int, int> action)
        {
            if (a.RowCount != b.RowCount || a.ColumnCount != b.ColumnCount)
            {
                throw new InvalidOperationException("Dimensions are not equal.");
            }

            Iterate(a.RowCount, a.ColumnCount, action);
        }

        private static IMatrixManipulations<T, Computable<T>> GetDeterminantManipulator(Matrix<T> matrix)
        {
            return DependencyConfiguration.DependencyResolver.Resolve<IMatrixManipulations<T, Computable<T>>>(
                new DependencyResolverParameterOverride("matrix", matrix),
                new DependencyResolverParameterOverride("value", matrix._determinant));
        }

        #endregion
    }
}